<?php

class CDatabaseMysql
{
	var $oConnexion;
	var $oRes;
	var $aDataRow = null;
	
	function __construct($bReel = false)
	{	
		$this -> oConnexion = mysql_pconnect(_SGBD_HOST, _SGBD_USER, _SGBD_PASS) ;//or trigger_error(mysql_error(), E_USER_ERROR);

		if(!$this -> oConnexion)
		{
			die("<font color='red'>Probl&egrave;me connexion base de donn&eacute;es</font><br><font size='1'><i>".mysql_error()."</i></font>");
		}
		
		mysql_select_db(_SGBD_BDD, $this -> oConnexion);
	}
	
	function __destruct()
	{
		// Destructeur
	}
	
	function _exec($sSql)
	{
			if (!$this->oConnexion)
				$this->showError('_exec appelé sans connection valide',$sSql);
				
			$query = mysql_query($sSql,$this->oConnexion);
			
			if (!$query)
				$this->showError($this->getError(),$sSql);
				
			return $query;
		}
		
		/**
		 * Fonction d'execution d'une requete SQL WRITE ONLY
		 *
		 * @param	String	$sSql	Requete SQL a executer.
		 * @param 	Array	Optionnel $avBindValues : valeur à affecter
		 * @return  int le nombre de ligne affecté si écriture
		 * @return  bool false en cas d'erreur
		 * @todo spécifier et implémenter $avBindValues
		 */
		function execute($sSql, $avBindValues=null)
		{
			$mRet = false;
						
			$oRes = $this->_exec($sSql);
			$bInsert = (strtolower(substr(trim($sSql),0,6))=='insert')? true : false;
			
			//Si on est dans un cas de retour true (update delete drop insert)
			if(!is_resource($oRes) and $oRes !== false)
			{
				$mRet =  $this->getAffectedRows();
				if($bInsert) $mRet = $this->lastInsertId();
			}
			
			return $mRet;
		}
		
		/**
		 * Retourne le nombre de lignes affectées
		 *
		 * @return int le nombre de lignes, false en cas d'erreur
		 */
		function getAffectedRows()
		{
			$nbligne = mysql_affected_rows($this->oConnexion);
			if($nbligne == -1) //erreur execution
			{
				$iLigne = 0;
			}
			else //ligne modifié = 1 ou 0 si déja le cas 
			{
				$iLigne = 1;
			}
			return $iLigne;
		}

		public function commit()
		{
      		return mysql_query("COMMIT", $this->oConnexion);
      	}
  
   		public function rollback()
   		{
      		return mysql_query("ROLLBACK", $this->oConnexion);
   		}
   		
		public function beginTransaction()
   		{
      		return mysql_query("START TRANSACTION" , $this->oConnexion);
   		}
		
		
		function setTransaction($asQueries, $bWithError=false)
		{
			//on initialise la transaction
			$iRetour = -1;
			//echo "debut TRANSACTION <br>iretour::".$iRetour;
			mysql_query("START TRANSACTION" , $this->oConnexion);
			
			if(is_array($asQueries))
			{
				
				foreach($asQueries as $i=>$sQuery)
				{
					//echo "<br>indice req::".$i." et iretour avant::". $iRetour;
					
            		$oRes = mysql_query($sQuery, $this->oConnexion);
            		//pour chaque requete unitaire executée, 
            		//on retourne l'index de la requete dans le tableau en cas d'erreur d'execution
            		if(mysql_affected_rows() == -1) { $iRetour = $i;  break;}
            		//echo "<br>indice req::".$i." et iretour apres::". $iRetour;
            		//echo "<br> sQuery::".$sQuery . " && iRetour::".$iRetour;
         		}
			}
			else 
			{//si l'argument transmis est une simple requete au lieu d'un tableau de requete
				$sSql = $asQueries;
				$oRes = mysql_query($sSql, $this->oConnexion);
				if(mysql_affected_rows() == -1){	$iRetour = 0; }
			}
			//si la ou les requetes ont toutes été executé avec succes 
			//la variable iRetour n'a pas changé de valeur -1 et donc on commit 
			//sinon  $iRetour > -1 et dans ce cas on rollback	
			if($iRetour !== -1)
			{
				
         		$this->rollback();
         		//echo "<br>iretour rollback::". $iRetour;
         		$mRetour = false;
     		}
     		else
     		{
         		$this->commit();
         		//echo "<br>iretour commit::". $iRetour;
         		$mRetour = true;
			}
			if($bWithError) // on demande à recupérer l'indice du tableau correspondant à la 1ere requete qui ne s'est pas exécuté
				$mRetour = $iRetour;
				
			return $mRetour;
		}
/**
		 * Fonction d'insertion de données dans une table.
		 *
		 * @param	String	$sTableName		Nom de la table dans laquelle inserer les données.
		 * @param	array	$aData			Données à inserer ($nomColonne=>$valeur).
		 * @param	array	$aDataType		Type de données à inserer ($nomColonne=>$typeDonnee)
		 * @param	array	$aPrimaryKey	Liste des clés primaires (inutile pour MySQL).
		 * @return	mixed	ID de la valeur insérée ou FALSE si l'insertion a échouée (utiliser l'opérateur !== false, car si la table n'a pas de champs AUTO_INCREMENT, la valeur retournée sera 0).
		 */
		function insert($sTableName, $aData, $aDataType=0, $aPrimaryKey=0)
		{
		//Initialisation de la variable de retour
			$mRet = false;
		//Initialisation du tableau contenant les noms des colonnes à insérer
			$aFieldsNames = array();
		//Initialisation du tableau contenant les valeurs à insérer
			$aFieldsValues = array();
		// Vérification que le tableau des types de données est bien un tableau
			if (!is_array($aDataType))
				$aDataType = array();
		//Pour chaque données à insérer, on effectue un traitement permettant de générer la requête d'insertion
			foreach ($aData as $sFieldName=>$sFieldValue)
			{
			//Ajout du nom de la colonne en cours de traitement, dans la liste des colonnes à insérer
				$aFieldsNames[] = $sFieldName;

			//Si le type de la colonne a été spécifié, alors on vérifie si l'ajout de quotes est nécessaire ou pas
				if (array_key_exists($sFieldName, $aDataType))
					$sFieldValue = $this->_formatValue($sFieldValue, $aDataType[$sFieldName]);
				else
					$sFieldValue = $this->_formatValue($sFieldValue);
//				Ajout de la valeur dans la liste des valeurs à insérer
				$aFieldsValues[] = $sFieldValue;

			}
			if(strpos($sTableName, '`.`') === false)
			{
				$sTableName = str_replace('.', '`.`', $sTableName);
			}
			
//			Construction de la requête d'insertion
			$sSql = 'INSERT INTO `'.$sTableName.'` ('
					. implode(', ', $aFieldsNames)
					. ') VALUES ('
					. implode(', ', $aFieldsValues)
					. ')';
//			Execution de la requête d'insertion
			$oRes = $this->_exec($sSql);
//			Si l'insertion est réussie, on récupére l'ID inséré, pour le retourner
			if ($oRes !== false)
				$mRet = $this->lastInsertId();
//			Si l'insertion est un echec, affichage d'un message d'erreur
			else
				$this->showError($this->getError(),$sSql);

			return $mRet;
		}
		

		function lastInsertId()
		{
			return mysql_insert_id($this->oConnexion);
		}
		
		/**
		 * Fonction de mise à jour de données d'une table.
		 *
		 * @param	String	$sTableName		Nom de la table dans laquelle modifier les données.
		 * @param	array	$aData			Données à modifier
		 * @param	String	$sWhere
		 * @param	array	$aDataType		Type de données à inserer ($nomColonne=>$typeDonnee)
		 * @return	mixed	Nombre de lignes modifiées ou FALSE si la modification a échouée (utiliser l'opérateur !== false, car si rien n'a été modifié, la valeur retournée sera 0).
		 * @see classes/DataBase#update($sTableName, $aData, $sWhere)
		 */
		function update($sTableName, $aData, $sWhere, $aDataType=0)
		{
//			Initialisation de la variable de retour
			$mRet = false;
//			Initialisation du tableau de données formatées (utilisé pour générer la requête SQL)
			$aDataFormated = array();
//			Vérification que le tableau des types de données est bien un tableau
			if (!is_array($aDataType))
				$aDataType = array();
			foreach ($aData as $sFieldName=>$sFieldValue)
			{
//				Si le type de la colonne a été spécifié, alors on vérifie si l'ajout de quotes est nécessaire ou pas
				if (array_key_exists($sFieldName, $aDataType))
					$sFieldValueFormated = $this->_formatValue($sFieldValue, $aDataType[$sFieldName]);
				else
					$sFieldValueFormated = $this->_formatValue($sFieldValue);
				$aDataFormated[] = "{$sFieldName}={$sFieldValueFormated}";
				unset($sFieldValueFormated);
			}

			if(strpos($sTableName, '`.`') === false)
			{
				$sTableName = str_replace('.', '`.`', $sTableName);
			}
			
			$sDataFormated = implode(', ', $aDataFormated);
			$sSql = 'UPDATE `'.$sTableName.'` SET '.$sDataFormated.' WHERE '.$sWhere;
			$oRes = $this->_exec($sSql);
			if ($oRes !== false)
				$mRet = mysql_affected_rows($this->oConnexion);
			else
				$this->showError($this->getError(),$sSql);
				
			return $mRet;
		}
		
		/**
		 * Fonction de suppression de données dans une table.
		 *
		 * @param	String	$sTableName		Nom de la table dans laquelle supprimer des données.
		 * @param	String	$sWhere
		 * @return	mixed	Nombre de lignes supprimées ou FALSE si la suppression a échouée (utiliser l'opérateur !== false, car si rien n'a été supprimé, la valeur retournée sera 0).
		 */
		function delete($sTableName, $sWhere)
		{
			$mRet = false;
			$sSql = 'DELETE FROM '.$sTableName.' WHERE '.$sWhere;
			$oRes = $this->_exec($sSql);
			if ($oRes !== false)
				$mRet = mysql_affected_rows($this->oConnexion);
			else
				$this->showError($this->getError(),$sSql);
			
			return $mRet;
		}
/**
		 * Fonction de formatage d'une valeur.
		 * Permet d'obtenir une valeur correcte pour générer une requête SQL.
		 *
		 * @param	String	$sValue		Valeur à formater
		 * @param	String	$sDataType	Type de donnée
		 * @return	String	Valeur formatée
		 */
		function _formatValue($sValue, $sDataType='')
		{
//			Initialisation de la variable de retour
			$sFormatedValue = $sValue;
//			Initialisation de la variable d'ajout ou non de quote autour de la valeur
			$bAddQuotes = false;
//			Vérification du type de la donnée			
			if($sValue === null)
			{
				return 'NULL';
			}
			
			if (substr($sDataType,0,3) == 'int')
			{
				$sFormatedValue = intval($sValue);
			}
			else if (substr($sDataType,0,7) == 'decimal')
			{
				$sFormatedValue = floatval($sValue);
			}
			else
			{
				switch (strtolower($sDataType))
				{
					case 'datetime': case 'date':
						
						$aMatches=array();
						//// FIXME Bidouille de daube en plus, ca marche plus en 2099
						//preg_match_all('/([0-3][0-9])\/([0-1][0-9])\/(20[0-9][0-9])/',$sValue,$aMatches);
						preg_match_all('/([0-9]{1,2})-([0-9]{1,2})-([0-9]{4})/', $sValue, $aMatches);
						if (isset($aMatches[0][0]) and $aMatches[0][0]==$sValue)
						{
							$sFormatedValue="str_to_date('$sValue','%d-%m-%Y')";
						}
						else if (strtolower($sValue) != 'now()')
							$bAddQuotes = true;
						break;
					default:
						$bAddQuotes = true;
						break;
				}
			}
//			S'il faut ajouter des quotes
			if ($bAddQuotes)
			{
				$sFormatedValue = addcslashes($sValue, '\'\\');
				$sFormatedValue = '\''.$sFormatedValue.'\'';
			}
			
			return $sFormatedValue;
		}
		
		function getError()
		{
			$sError='';
			
			if ($this->oConnexion)
				$sError=mysql_error($this->oConnexion);
			else
				$sError='Pas de connexion mysql';
			return $sError;
		}
/**
     * Protection de données avant insertion ou mise à jour.
     *
     * Prépare une chaine de caractère pour une requête UPDATE ou INSERT
     *
     * @param  String $sChaine
     * @return String
     */
    public function text( $sChaine )
    {
        return $this->_formatValue($sChaine,'varchar');
    }
    
	public function date( $sChaine, $sType ='date' )
    {
        return $this->_formatValue($sChaine, $sType);
    }
    
	public function float( $sChaine )
    {
        return $this->_formatValue($sChaine, 'decimal');
    }
    
	public function escape_null( $sChaine )
    {
        return $this->_formatValue($sChaine);
    }
		/**
	 * 
	 * 
	 * @param $sError
	 * @param $sSql
	 * @return unknown_type
	 */
	function showError($sError, $sSql='')
	{
		$sErrorComplement = 'Error in class '.get_class($this);
		$sErrorComplement.= $this->getError();
		
		if ($sSql != '')
			$sErrorComplement .= ' SQL=['.$sSql.'] ';
			
		if ($sError!='')
			$sError=' Error was ['.$sError.'] ';
			
		print($sErrorComplement.$sError);
	}
	
	function escapeString($sStr)
	{
			return mysql_real_escape_string($sStr);
	}
	
	function query($sSql,$avBindValues=null, $sCacheTime = -1)
	{
			$mRet = false;
			
			if($mRet === false)
			{
				$oRes = $this->_exec($sSql);
				//Si on est dans un cas de lecture (select show table ...)
				if ($oRes !== false)
				{
					$mRet = $this->DataSourceFetchMysql($oRes);
				}
			}
			else
			{
			//	print "mode cache".cr();
			}
			return $mRet;
		}
	
		function DataSourceFetchMysql($oRes=null)
		{
			$this->oRes = false;
			if ($this->_isMysqlResult($oRes))
				$this->oRes = $oRes;
			else
				$this->showError('N\'est pas une ressource MySQL valide.', true);
			$this->aDataRow = array();
			while($aRow = mysql_fetch_assoc($this->oRes))
			{
				$this->aDataRow[] = $aRow;
			}
			return $this->aDataRow;
		}
		
		function _isMysqlResult(&$oRes)
		{
			$bRet = false;
	
			if (is_resource($oRes))
			{
				if (get_resource_type($oRes) === 'mysql result')
				{
					$bRet = true;
				}
			
			}
			return $bRet;
		}
}
?>